/*
    node 的 mySql  使用 (数据库)

    速查表
    https://www.runoob.com/nodejs/nodejs-express-framework.html   //Express
    https://www.runoob.com/nodejs/nodejs-mysql.html   //连接 MySQL
    https://www.runoob.com/sql/sql-tutorial.html    //写SQL

    开发需求：通过配置数组的形式对数据库表单增、删、改、查，能快速生成满足资讯类站点需求的基础API
*/
// http://127.0.0.1:2101/api/v1/
// 生成动态API （使用node调数据库，生成数据API）

// 配置基础API直调数据库表(默认加增、删、改、查API功能)（非基础API功能另开发）
// const apiArray = ['floor', 'sort', 'store', 'news', 'active', 'notice', 'banner', 'login', 'conversion', 'message', 'synopsis', 'traffic', 'attract', 'attract', 'site', 'contact', 'loginfo',  'joinUs','roles','aboutus'] 

//建单表sql
// CREATE TABLE `active` (
//   `id` TINYINT(4) NOT NULL AUTO_INCREMENT,
//   `title` VARCHAR(200) DEFAULT NULL,
//   `date` VARCHAR(50) DEFAULT NULL,
//   `value` TEXT,
//   `img` VARCHAR(200) DEFAULT NULL,
//   `sort` INT(5) DEFAULT NULL,
//   `issue` INT(5) DEFAULT NULL,
//   `top` INT(5) DEFAULT NULL,
//   PRIMARY KEY (`id`)
// ) ENGINE=MYISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

const apiArray = ['login','loginfo','oplog','roles','message','banner','table_aboutus','table_show','table_events','table_serve','table_partner'] 

//Web应用框架
const express = require('express') 
const app = express() //实例化express应用
app.use('/public', express.static('public')) // 设置图片、视频、附件等静态资源存放路径
const bodyParser = require('body-parser') // post中间件，用于post解析参数
// 处理post请求数据解析
app.use(bodyParser.json()) // JSON请求
app.use(bodyParser.urlencoded({extended: false})) // 表单请求

const fs = require('fs') //文件系统模块

// 在处理模块中引入第三方解析模块
const multipart = require('connect-multiparty') 
const multipartMiddleware = multipart() // post数据解析

// cors跨域支持
const cors = require('cors') 
app.use(cors()) // 解决跨域(替换下面的处理方案)

// 数据库系统模块
const mysql = require('mysql') 
const sqlConfig = { /* 配置数据库*/
  host: 'localhost', // 主机地址
  user: 'king11',
  password: '@kingdom10',
  database: 'dev-dz', // 数据库名
  port:3306
}
/* 数据库重连机制(防止数据库断开)*/
let conn,pool,que
function reconn() {
  conn = mysql.createPool(sqlConfig) // 数据库连接池
  // 封装
  que = function (sql, callback) {
    pool.getConnection(function (error, conn) { // 从连接池中取出连接,如无连接可用则隐式的建立一个数据库连接。
      conn.query(sql, function (error, results) {
        callback(error, results) // 结果回调
        conn.release() // 释放连接资源 | 跟 conn.destroy() 不同，它是销毁
      })
    })
  }
  
  conn.on('error', error => { // 数据库断开的回调
    console.log('数据库重连' + error)
    setTimeout(reconn, 1000) // 递归调用数据库连接
  })
}
reconn()

// API版本号
const versions = '/v1/'
// (数据池)存储所有数据库表数据
const dataArray = []

// 排序数据
function sortData(rows){
  return JSON.parse(JSON.stringify(rows)).sort((a, b) => {
      a = new Date(a.date).getTime()
      b = new Date(b.date).getTime()
      return b - a
    }).sort((a, b) => {
      return b.sort - a.sort
    }).sort((a, b) => { //勾选TOP优先级最高
      return b.top - a.top
    })    
}
/* 根据表名(对应API名)更新数据池（查询数据库） */
function tableDataUpdate(item) {
  conn.query('SELECT * FROM ' + item, function (error, rows) { // 读取数据库
    if (error) throw error;
    let data = sortData(rows);//排序数据
    return dataArray[apiArray.indexOf(item)] = data //按数据库表名数组索引记录数据
  })
}

//格式化请求参数value值，使其符合sql查询字段规范
function ObjectToSqlString(da){
  for(let key in da){ 
    if(typeof da[key] === 'boolean'){//布尔值转存0、1
      da[key] = Number(da[key])
    }else if(typeof da[key] === 'string'){ //给sql的string值加''号
      da[key] = '\'' + da[key] + '\''
    }
  }
  return da
} 

/* 修改内容数据接口*/
function putAPI(item) {
  app.put(versions + item, function (req, res) { // 建立数据接口
    let da = req.body // 请求体 数据案例 { top: false, id: [ 18, 16 ] }  || { top: false, id: 16 }
    const idUl = Array.isArray(da.id) ? da.id.join(",") : da.id //多个ID则用','合并ID
    let te = '';
    da = ObjectToSqlString(da); //格式化请求参数value值
    for(let key in da){    
      if(key === 'id') continue; //ID不参与拼接   
      te += "," + key + "=" + da[key] //拼接sql查询字段
    }
    te = te.slice(1) //去掉第一个','
    //UPDATE websites SET issue=1  WHERE id IN (19,15,16)  批量更新sql
    //UPDATE floor SET title="L21",sort=10,issue=1,top=0,date="2021-06-23 17:36:52" WHERE id IN (18) 多字段更新
    console.log('UPDATE ' + item + ' SET ' + te + ' WHERE id IN (' + idUl + ")")    
    conn.query('UPDATE ' + item + ' SET ' + te + ' WHERE id IN (' + idUl + ")",function (error) { // 修改指定数据
      if (error) throw error;
      if(item === 'roles'){ //roles表关联login表修改用户数据
        alterUserData('login',da)
      }
      tableDataUpdate(item) // 刷新单个数据池
      res.status(200)
      res.json()
    })
  })
}

/* 插入数据接口*/
function addAPI(item) {
  app.post(versions + item, function (req, res) { // 建立数据接口
    let da = req.body // 请求体  
    let te = '' //查询字段
    let valTe = '' //对应值
    da = ObjectToSqlString(da); //格式化请求参数value值
    for (let key in da) { // sql字段拼接
      if(key === 'id') continue; //ID不参与拼接(ID在数据库设置自增长)
      te += ',' + key //拼接key
      valTe += ',' + da[key] //拼接value
    }   
    te = te.slice(1)
    valTe = valTe.slice(1) //去掉第一个','


    // 'INSERT INTO oplog(username,text,date)  VALUES('admin','新增内容：主力店（成功）','2021-11-05 11:03:50')'
    console.log('INSERT INTO ' + item + '(' + te + ')  VALUES(' + valTe + ')')
    conn.query('INSERT INTO ' + item + '(' + te + ')  VALUES(' + valTe + ')', function (error,result) { // 修改指定数据
      if (error) throw error;      
      if(item === 'roles'){ //roles表关联login表添加用户数据
        da.rid = JSON.parse(JSON.stringify(result)).insertId //记录自增ID
        addUserData('login',da)
      }
      tableDataUpdate(item) // 刷新单个数据池
      res.status(200)
      res.json()     
    })
    
  })
}

// 数据删除接口
function deleteAPI(item) {
  app.delete(versions + item, function (req, res) { // 建立数据接口
    const da = req.query // 请求数据
    // DELETE FROM websites where id=6
    conn.query('DELETE FROM ' + item + ' WHERE id= ' + da.id, function (error,result) { // 删除指定数据
      if (error) throw error;
      if(item === 'roles'){ //roles表关联login表删除用户数据
        da.rid = JSON.parse(JSON.stringify(result)).insertId //记录自增ID
        deleteUserData('login',da)
      }
      tableDataUpdate(item) // 刷新单个数据池
      res.status(200)
      res.json()      
    })
  })
}

// 常规get数据接口
function getAPI(item){
  app.get(versions + item, function (req, res) { // 建立数据接口
    res.status(200)    
    let data = dataArray[apiArray.indexOf(item)]; //当前API返回的数据    
    /* 筛选发布数据 */
    if(data.length > 0 && !(data[0].issue === void 0) && req.query.all != 1){ //通过 all=1 可以获取全部数据（包括待发布数据）（用于后台显示数据）     
      data = data.filter(function(item){  //前台只显示发布数据
        return parseInt(item.issue) === 1;
      })
    }
    res.status(200)
    res.json(data) // 响应头返回相应查询数据
  })
}

// 批量生成增删改查API
apiArray.forEach((item) => {
  tableDataUpdate(item) // 初始化数据池
  getAPI(item) //查
  deleteAPI(item) // 删
  if (item != 'login') { // 登录用户数据单独处理
    putAPI(item) // 改
    addAPI(item) // 增
  }  
})

/* 角色表需要关联操作登陆表用户数据 */
/* 添加用户数据 */
function addUserData(tableName,da){
  console.log('INSERT INTO ' + tableName + '(username,password,date,rid) VALUES(' + da.username + ',' + da.password + ',' + da.date + ',' + da.rid + ')')
  conn.query('INSERT INTO ' + tableName + '(username,password,date,rid) VALUES(' + da.username + ',' + da.password + ',' + da.date + ',' + da.rid + ')', function (error) { // 修改指定数据
    if (error) throw error;
    tableDataUpdate(tableName) // 刷新单个数据池
  })
}
/* 修改用户数据 */
function alterUserData(tableName,da){
  console.log('UPDATE ' + tableName + ' SET username=' + da.username + ',password=' + da.password + ',date=' + da.date + ' WHERE rid IN (' + da.id + ')') //使用关联rid修改用户数据
  conn.query('UPDATE ' + tableName + ' SET username=' + da.username + ',password=' + da.password + ',date=' + da.date + ' WHERE rid IN (' + da.id + ')', function (error) { // 修改指定数据
    if (error) throw error;
    tableDataUpdate(tableName) // 刷新单个数据池
  })
}
/* 删除用户数据 */
function deleteUserData(tableName,da){
  console.log('DELETE FROM ' + tableName + ' WHERE rid= ' + da.id) //使用关联rid删除用户数据
  conn.query('DELETE FROM ' + tableName + ' WHERE rid= ' + da.id, function (error) { 
    if (error) throw error;
    tableDataUpdate(tableName) // 刷新单个数据池
  })
}
/* 修改密码(表名，请求头数据对象) */
function alterPassword(tableName,da){
  console.log('UPDATE ' + tableName + ' SET password=' + da.setPassword + ' WHERE username IN (' + da.username + ')') //使用关联rid修改用户数据
  conn.query('UPDATE ' + tableName + ' SET password=' + da.setPassword + ' WHERE username IN (' + da.username + ')', function (error) { // 修改指定数据
    if (error) throw error;
    tableDataUpdate(tableName) // 刷新单个数据池
  })
}


// 在systemData.js读取系统信息并module.exports系统信息对象（拆分js）
const systemInfo = new(require('./systemData'))() 
app.get(versions + 'system', function (req, res) { // 返回系统信息
  res.status(200),
    res.json(systemInfo)
})

/* 列名查询 */
apiArray.forEach(function (item) {
  app.get(versions + item + 'Head', function (req, res) { // 建立数据接口
    conn.query('SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME="' + item + '"', function (error, rows) { // 读取数据库
      if (error) throw error;
      res.json(rows)      
    })
  })
})

//返回响应头数据（请求头数据）
function returnResData(reqBody){
  const logDa = dataArray[apiArray.indexOf('login')] // 数据库用户数据
  const resData = {} // 响应数据 
  const logAr = [
    [], // 记录登录成功与否（1：成功，0：失败）
    [], // 记录用户名是否存在（1：存在，0：不存在）
    [] // 记录密码是否正确（1：正确,0:错误）
  ]
  
  logDa.forEach(function (item) {
    //记录是否登录成功
    if(reqBody.username == item.username && reqBody.password == item.password){
      logAr[0].push(1)  
      //添加登录成功返回头数据
      resData.data = {
        id:item.id,
        rid:item.rid,
        username:item.username
      }      
    }else{
      logAr[0].push(0)
    }     
    //记录用户名是否存在 
    if (reqBody.username == item.username) {
      logAr[1].push(1)
      // 记录密码是否正确
      reqBody.password == item.password ? logAr[2].push(1) : logAr[2].push(0)              
    } else {
      logAr[1].push(0)
    }
  })
  if (logAr[0].includes(1)) {
    resData.meta = {
      'status': 200,
      'message': '用户登录成功'      
    }    
  } else if (!logAr[1].includes(1)) {
    resData.meta = {
      'status': 400,
      'message': '用户名不存在'
    }
  } else if (!logAr[2].includes(1)) {
    resData.meta = {
      'status': 400,
      'message': '密码错误'
    }
  }
  return resData //返回响应头数据
}
// 用户登录数据接口
app.post(versions + 'login', function (req, res) { // 建立数据接口
  // req.params.id(get请求参数 /:id)  req.query.id(get请求 ?id=0)   req.body.id(post请求体)
  const reqBody = req.body // post请求数据
  let resData = {} // 响应数据 
  resData = returnResData(reqBody); //返回响应数据
  if(resData.meta.status == 200){ //为登陆成功响应数据加token值
    resData.data.token = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE1MTI1NDQyOTksImV4cCI6MTUxMjYzMDY5OX0.eGrsrvwHm-tPsO9r_pxHIQ5i5L1kX9RX444uwnRGaIM'
  }  
  console.log(resData)
  res.json(resData) // 以json形式发送响应数据
})

// 修改密码数据接口
app.put(versions + 'login', function (req, res) { // 建立数据接口
  let reqBody = req.body // post请求数据
  let resData = {} // 响应数据  
  resData = returnResData(reqBody); //返回响应数据
  if(resData.meta.status === 200){//为成功响应执行修改密码操作
    resData.meta.message = "密码修改成功"
    reqBody = ObjectToSqlString(reqBody); //格式化请求数据
    alterPassword('login',reqBody);//修改登陆密码
    alterPassword('roles',reqBody) //关联修改角色表用户密码
    tableDataUpdate('login') // 刷新数据池单个数据
  }
  res.json(resData) // 以json形式发送响应数据
})

/* 文件上传 */
let response
app.post(versions + 'file_upload', multipartMiddleware, function (req, res) { // 文件上传
  //console.log(req.files.file) // 上传文件信息
  const r = new Date().getTime(); // 定文件唯一路径
  const des_file = '/serves/images/' + r + req.files.file.originalFilename // 文件存放相对路径
  fs.readFile(req.files.file.path, function (error, data) {
    fs.writeFile(__dirname + des_file, data, function (error) { // _dirname （写入需绝对路径，把相对路径转换成绝对路径）
      if (error) throw error;
      response = {
        message: '文件上传成功',
        data: des_file
      }      
      //console.log(response)
      res.json(response)
    })
  })
})

/* neditor编辑器 */
let buf = new Buffer.alloc(3000)
fs.open(__dirname + '/ueConfig.json', 'r+', function (error, fd) { // 打开ueditor配置文件
  if (error) throw error;
  // console.log("配置文件打开成功");
  fs.read(fd, buf, 0, buf.length, 0, function (error, bytes) { // 读取配置
    if (error) throw error;
    // console.log(bytes + "  字节被读取");
    app.get(versions + 'ueditor', function (req, res) { // 文件上传
      const daOb = JSON.parse(buf.slice(0, bytes).toString())
      res.jsonp(daOb) // 传送JSONP响应
    })
  })
})

// 编辑器文件上传
app.post(versions + 'ueditor', multipartMiddleware, function (req, res) { 
  // const action = req.query.action;
  // if (action == 'uploadimage' || action == 'uploadvideo' || action == 'uploadfile') { //图片/视频/附件上传判断
  //console.log(req.is())
  //console.log(req.files.upfile)
  const r = new Date().getTime() + '_' // 定文件唯一路径
  const des_file = '/serves/images/' + r + req.files.upfile.originalFilename // 文件存放相对路径
  fs.readFile(req.files.upfile.path, function (error, data) {
    fs.writeFile(__dirname + des_file, data, function (error) { // _dirname （写入需绝对路径，把相对路径转换成绝对路径）
      if (error) throw error;
      let responseImg = { //要按neditor后端请求规范返回响应数据
        code: '200',
        message: 'SUCCESS',
        url: des_file,
        thumbnail: des_file,
        title: req.files.upfile.originalFilename,
        original: req.files.upfile.originalFilename,
        error: error
      }            
      res.jsonp(responseImg)
    })
  })
})

// conn.end(); //关闭数据库

//统一的异常处理，防止程序崩溃
process.on('uncaughtException', function (err) { 
  //打印出错误
  console.log(err) 
});


// 配置服务端口
app.listen(8008, () => {
  console.log('server :localhost:8008/v1/')
})